Release 10.1A: OpenEdge Development:
ProDataSets
Building the server side change handler
On the server side, there can also be a generic procedure that applies all changes to a ProDataSet. As we explained in Chapter 6, "Updating Data with ProDataSets,", there is a
SAVE-ROW-CHANGESmethod to apply one row of changes to the database, but not aSAVE-CHANGESmethod for a temp-table or for the entire ProDataSet. The reason for this is there might be many ways in which you need to control the update process, including:
- The transaction scope — Are all updates made as part of a single transaction that fails in its entirety if any change is not valid? Are all updates to a single table made together? Or is each individual change a separate transaction that can succeed or fail independent of others? These questions are the reason why we support both an
ERRORand aREJECTEDattribute for ProDataSet rows. A change that fails or conflicts with a change made by another user isREJECTEDand also made anERROR. Any other change that is backed out because it is part of the same transaction isREJECTEDwhether it caused an error or not. You can check these flags back in the caller to understand what made it into the database and what didn’t, and why.- The order of applying changes to different tables — In a parent-child ProDataSet or when there are multiple top-level tables, which changes should be applied first? Normally, you would apply changes from the top down, but there might be exceptions to that. And if there are multiple top-level tables, does it matter to you which order changes are applied in? By writing your own code you have complete control over this. No default would satisfy all situations.
- The order of creates, deletes, and modifies to a single table — Do you want deletes applied before or after creates and updates? This might depend on your data definitions or business logic. The
ROW-STATEattribute has a numeric value that orders rows so that unmodified rows are first, followed by deleted rows, modified rows, and created rows, in that order. You can take advantage of this default ordering, but you can also change it as you need to.This section shows you a sample for a general purpose procedure that makes default decisions about these variants. Each row is a single transaction. Tables are processed from the top of the hierarchy down (and multiple top-level tables are processed in the order in which they appear in the ProDataSet definition). And changes are processed in
ROW-STATEOrder. You could, of course, extend or modify this procedure for other defaults and to accept parameters to change the behavior.The example procedure is
commitChanges.p. It handles changes to any ProDataSet with any number of tables in its hierarchy. It takes the ProDataSet handle as itsINPUT-OUTPUTparameter. The variables represent the handle to the current top-level buffer and a buffer counter, as shown:
The procedure starts its main loop through all top-level buffers, using the
NUM-TOP-BUFFERScounter and theGET-TOP-BUFFERmethod to return each one in turn. These are the temp-table buffers with no parent. They are returned in the order they appear in the ProDataSet definition. There is one special case for which the loop must check. Because of theFILLbehavior, children of aREPOSITIONData-Relation show up in the list of top-level buffers, even though they aren’t really top-level for the purpose of walking through the hierarchy of the ProDataSet as this procedure does. For this reason, the loop contains a check to see if there is aPARENT-RELATIONfor the buffer. If there is, then it’s really a child of a reposition relation, not a true top-level buffer, and it is skipped.For each top-level buffer, the recursive procedure
traverseBuffersis run to walk down that branch of the ProDataSet definition, as shown:
Procedure
traverseBuffersserves only to recurse down through any number of levels of parent-child tables. It runs another internal proceduresaveBufferon itself and then loops through each child of the current buffer.NUM-CHILD-RELATIONSreturns the number of relations for which this buffer is the parent, andGET-CHILD-RELATIONreturns each one in turn. SinceGET-CHILD-RELATIONreturns the handle of the Data-Relation object, you need to follow that to itsCHILD-BUFFERto get the buffer handle of each of the current’s buffer’s children, as shown:
Procedure
saveBufferdoes the actual work of runningSAVE-ROW-CHANGES, as shown:
The buffer handle passed in is actually the after-table buffer for the current table. You want to run
SAVE-ROW-CHANGESon the before-table buffer, especially since in the case of aDeletethere is no after-table row to process. VariablehBeforeBuffpoints to that buffer.The
VALID-HANDLEtest checks whether there is a before-table for this temp-table at all. If the table does not have aBEFORE-TABLEin its definition and has not been enabled for update by settingTRACKING-CHANGESto true, then there will not be a before-table. Otherwise, the before-table could contain zero, one, or many rows, depending on how many rows in that table have been changed. The query simply walks through all those rows and runsSAVE-ROW-CHANGESon each one.SAVE-ROW-CHANGESstarts its own transaction if there is none active, so each change is saved independently. If the save fails because of invalid data or because it had been changed by another user, Progress sets theERRORattribute on the row. The procedure checks this and also sets theREJECTEDattribute, so that the caller knows row by row which rows were successfully updated into the database and which ones were not, as shown:
Remember that
SAVE-ROW-CHANGEStakes two optional arguments:Because this is a generic save procedure, there is no straightforward way to specify these parameters if they’re needed. This example uses the default.
That’s the end of the code for
commitChanges.p.
|
Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095 |